// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#include <nebula/core/config.h>

#include <cstdint>

#include <nebula/version.h>
#include <nebula/util/cpu_info.h>
#include <nebula/datetime/datetime.h>

namespace nebula {

    using internal::CpuInfo;

    namespace {

        const BuildInfo kBuildInfo = {
                // clang-format off
                NEBULA_VERSION_MAJOR,
                NEBULA_VERSION_MAJOR,
                NEBULA_VERSION_MINOR,
                NEBULA_VERSION_PATCH,
                NEBULA_VERSION_STRING,
                NEBULA_SO_VERSION,
                NEBULA_FULL_SO_VERSION,
                NEBULA_CXX_COMPILER_ID,
                NEBULA_CXX_COMPILER_VERSION,
                NEBULA_CXX_COMPILER_FLAGS,
                "NEBULA_GIT_ID",
                "NEBULA_GIT_DESCRIPTION",
                NEBULA_PACKAGE_KIND,
                NEBULA_BUILD_TYPE,
                // clang-format on
        };

        template<typename QueryFlagFunction>
        std::string MakeSimdLevelString(QueryFlagFunction &&query_flag) {
            if (query_flag(CpuInfo::AVX512)) {
                return "avx512";
            } else if (query_flag(CpuInfo::AVX2)) {
                return "avx2";
            } else if (query_flag(CpuInfo::AVX)) {
                return "avx";
            } else if (query_flag(CpuInfo::SSE4_2)) {
                return "sse4_2";
            } else {
                return "none";
            }
        }

        std::optional<std::string> timezone_db_path;

    };  // namespace

    const BuildInfo &GetBuildInfo() { return kBuildInfo; }

    RuntimeInfo GetRuntimeInfo() {
        RuntimeInfo info;
        auto cpu_info = CpuInfo::GetInstance();
        info.simd_level =
                MakeSimdLevelString([&](int64_t flags) { return cpu_info->is_supported(flags); });
        info.detected_simd_level =
                MakeSimdLevelString([&](int64_t flags) { return cpu_info->IsDetected(flags); });
        info.using_os_timezone_db = USE_OS_TZDB;
#if !USE_OS_TZDB
        info.timezone_db_path = timezone_db_path;
#else
        info.timezone_db_path = std::optional<std::string>();
#endif
        return info;
    }

    turbo::Status Initialize(const GlobalOptions &options) noexcept {
        if (options.timezone_db_path.has_value()) {
#if !USE_OS_TZDB
            try {
              alkaid_vendored::date::set_install(options.timezone_db_path.value());
              alkaid_vendored::date::reload_tzdb();
            } catch (const std::runtime_error& e) {
              return turbo::io_error(e.what());
            }
            timezone_db_path = options.timezone_db_path.value();
#else
            return turbo::invalid_argument_error(
                    "Nebula was set to use OS timezone database at compile time, "
                    "so a downloaded database cannot be provided at runtime.");
#endif  // !USE_OS_TZDB
        }
        return turbo::OkStatus();
    }

}  // namespace nebula
